/**
 ******************************************************************************
 * @file           : bsp_spi.c
 * @brief          : SPI Driver Implementation (STM32F1xx)
 ******************************************************************************
 * @attention
 *
 * SPI驱动程序实现文件
 * 提供SPI主机模式通信功能
 * 支持阻塞式收发和完整的错误处理
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/SPI/bsp_spi.h"
#include "./BSP/SPI/bsp_spi_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static SPI_HandleTypeDef sg_spi_handle;                     /*!< SPI句柄 */
static volatile SPI_State_e sg_spi_state = SPI_STATE_IDLE;  /*!< SPI当前状态 */
static volatile uint8_t sg_spi_initialized = 0;             /*!< 初始化标志 */

#if (SPI_USE_DMA == 1)
static DMA_HandleTypeDef sg_spi_dma_tx_handle;              /*!< SPI DMA TX句柄 */
static volatile uint8_t sg_dma_transfer_done = 1;           /*!< DMA传输完成标志 */
#endif

/* Private function prototypes -----------------------------------------------*/
static void spi_gpio_init(void);
static void spi_peripheral_init(void);

#if (SPI_USE_DMA == 1)
static void spi_dma_init(void);
#endif

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       SPI GPIO初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置SCK、MISO、MOSI、CS引脚
 */
static void spi_gpio_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    /* 配置引脚重映射（如果需要） */
    SPI_PIN_REMAP_CONFIG();

    /* 使能GPIO时钟 */
    SPI_SCK_GPIO_CLK_ENABLE();
    SPI_MOSI_GPIO_CLK_ENABLE();
    SPI_CS_GPIO_CLK_ENABLE();
    
#if (SPI_DIRECTION_MODE != 1)
    /* 双线模式才需要MISO */
    SPI_MISO_GPIO_CLK_ENABLE();
#endif

    /* 配置SCK引脚 - 复用推挽输出 */
    gpio_init_struct.Pin = SPI_SCK_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &gpio_init_struct);

#if (SPI_DIRECTION_MODE != 1)
    /* 配置MISO引脚 - 输入（仅双线模式） */
    gpio_init_struct.Pin = SPI_MISO_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &gpio_init_struct);
#endif

    /* 配置MOSI引脚 - 复用推挽输出 */
    gpio_init_struct.Pin = SPI_MOSI_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &gpio_init_struct);

    /* 配置CS引脚 - 通用推挽输出 */
    gpio_init_struct.Pin = SPI_CS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SPI_CS_GPIO_PORT, &gpio_init_struct);

    /* CS默认拉高（未选中） */
    SPI_CS_HIGH();
}

/**
 * @brief       SPI外设初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置SPI工作模式和参数
 */
static void spi_peripheral_init(void)
{
    /* 使能SPI时钟 */
    SPI_SPIx_CLK_ENABLE();

    /* 配置SPI参数 */
    sg_spi_handle.Instance = SPI_SPIx;
    sg_spi_handle.Init.Mode = SPI_MODE_MASTER;                      /* 主机模式 */
    sg_spi_handle.Init.Direction = SPI_DIRECTION;                   /* 方向模式（单线/双线）*/
    sg_spi_handle.Init.DataSize = SPI_DATASIZE;                     /* 数据位宽 */
    sg_spi_handle.Init.CLKPolarity = SPI_CPOL;                      /* 时钟极性 */
    sg_spi_handle.Init.CLKPhase = SPI_CPHA;                         /* 时钟相位 */
    sg_spi_handle.Init.NSS = SPI_NSS_SOFT;                          /* 软件控制CS */
    sg_spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATE_PRESCALER;  /* 波特率分频 */
    sg_spi_handle.Init.FirstBit = SPI_FIRST_BIT;                    /* 传输顺序 */
    sg_spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;                 /* 禁用TI模式 */
    sg_spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁用CRC */

    /* 初始化SPI */
    if (HAL_SPI_Init(&sg_spi_handle) != HAL_OK)
    {
        log_e("SPI外设初始化失败");
    }
}

#if (SPI_USE_DMA == 1)
/**
 * @brief       SPI DMA初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置DMA用于SPI发送，大幅提升传输性能
 */
static void spi_dma_init(void)
{
    /* 使能DMA时钟 */
#if (SPI_SELECT_NUM == 1) || (SPI_SELECT_NUM == 2)
    __HAL_RCC_DMA1_CLK_ENABLE();
#elif (SPI_SELECT_NUM == 3)
    __HAL_RCC_DMA2_CLK_ENABLE();
#endif

    /* 配置DMA TX通道 */
    sg_spi_dma_tx_handle.Instance = SPI_DMA_TX_CHANNEL;
    sg_spi_dma_tx_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;        /* 内存到外设 */
    sg_spi_dma_tx_handle.Init.PeriphInc = DMA_PINC_DISABLE;            /* 外设地址不递增 */
    sg_spi_dma_tx_handle.Init.MemInc = DMA_MINC_ENABLE;                /* 内存地址递增 */
    sg_spi_dma_tx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设数据宽度8位 */
    sg_spi_dma_tx_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;  /* 内存数据宽度8位 */
    sg_spi_dma_tx_handle.Init.Mode = DMA_NORMAL;                       /* 正常模式 */
    sg_spi_dma_tx_handle.Init.Priority = DMA_PRIORITY_HIGH;            /* 高优先级 */

    /* 初始化DMA */
    if (HAL_DMA_Init(&sg_spi_dma_tx_handle) != HAL_OK)
    {
        log_e("SPI DMA初始化失败");
        return;
    }

    /* 关联DMA和SPI */
    __HAL_LINKDMA(&sg_spi_handle, hdmatx, sg_spi_dma_tx_handle);

    /* 配置DMA中断优先级并使能 */
#if (SPI_SELECT_NUM == 1)
    HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
#elif (SPI_SELECT_NUM == 2)
    HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
#elif (SPI_SELECT_NUM == 3)
    HAL_NVIC_SetPriority(DMA2_Channel2_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(DMA2_Channel2_IRQn);
#endif

    log_d("SPI DMA初始化完成");
}
#endif

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       SPI初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        初始化SPI GPIO和外设
 */
void spi_init(void)
{
    if (sg_spi_initialized)
    {
        log_w("SPI已经初始化");
        return;
    }

    /* 初始化GPIO */
    spi_gpio_init();

    /* 初始化SPI外设 */
    spi_peripheral_init();

#if (SPI_USE_DMA == 1)
    /* 初始化DMA */
    spi_dma_init();
#endif

    /* 设置状态 */
    sg_spi_state = SPI_STATE_IDLE;
    sg_spi_initialized = 1;

    log_i("SPI初始化完成");
}

/**
 * @brief       SPI反初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        释放SPI资源
 */
void spi_deinit(void)
{
    if (!sg_spi_initialized)
    {
        return;
    }

    /* 反初始化SPI */
    HAL_SPI_DeInit(&sg_spi_handle);

    /* 反初始化GPIO */
    HAL_GPIO_DeInit(SPI_SCK_GPIO_PORT, SPI_SCK_GPIO_PIN);
#if (SPI_DIRECTION_MODE != 1)
    HAL_GPIO_DeInit(SPI_MISO_GPIO_PORT, SPI_MISO_GPIO_PIN);
#endif
    HAL_GPIO_DeInit(SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PIN);
    HAL_GPIO_DeInit(SPI_CS_GPIO_PORT, SPI_CS_GPIO_PIN);

    /* 清除标志 */
    sg_spi_initialized = 0;
    sg_spi_state = SPI_STATE_IDLE;

    log_i("SPI已反初始化");
}

/**
 * @brief       选中SPI从机
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        拉低CS信号
 */
void spi_cs_select(void)
{
    SPI_CS_LOW();
}

/**
 * @brief       取消选中SPI从机
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        拉高CS信号
 */
void spi_cs_deselect(void)
{
    SPI_CS_HIGH();
}

/**
 * @brief       SPI读写单字节
 *
 * @param[in]   tx_data 要发送的数据
 *
 * @retval      接收到的数据
 *
 * @note        SPI全双工通信，发送的同时接收
 */
uint8_t spi_read_write_byte(uint8_t tx_data)
{
    uint8_t rx_data = 0;

    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return 0;
    }

    /* 全双工发送接收 */
    if (HAL_SPI_TransmitReceive(&sg_spi_handle, &tx_data, &rx_data, 1, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_w("SPI读写字节失败");
        sg_spi_state = SPI_STATE_ERROR;
        return 0;
    }

    return rx_data;
}

/**
 * @brief       SPI写多字节
 *
 * @param[in]   data 数据指针
 * @param[in]   len  数据长度
 *
 * @retval      SPI_OK         写入成功
 * @retval      SPI_ERROR_PARAM 参数错误
 * @retval      SPI_ERROR_HAL   HAL库错误
 *
 * @note        只发送不接收
 */
SPI_Error_e spi_write(const uint8_t *data, uint16_t len)
{
    /* 参数检查 */
    if (data == NULL || len == 0)
    {
        log_w("写入参数错误: data=%p, len=%d", data, len);
        return SPI_ERROR_PARAM;
    }

    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }

    sg_spi_state = SPI_STATE_TX;

#if (SPI_USE_DMA == 1)
    /* DMA模式：同步传输（等待完成，稳定可靠）*/
    uint32_t timeout_start;
    uint32_t timeout_ms = 1000;  /* 超时时间1秒 */
    
    /* 等待上次DMA传输完成（带超时检测） */
    timeout_start = HAL_GetTick();
    while (sg_dma_transfer_done == 0)
    {
        if ((HAL_GetTick() - timeout_start) > timeout_ms)
        {
            /* 超时：强制复位DMA状态 */
            log_e("等待DMA完成超时");
            sg_dma_transfer_done = 1;
            HAL_SPI_DMAStop(&sg_spi_handle);
            sg_spi_state = SPI_STATE_ERROR;
            return SPI_ERROR_TIMEOUT;
        }
    }
    
    /* 开始新的DMA传输 */
    sg_dma_transfer_done = 0;
    if (HAL_SPI_Transmit_DMA(&sg_spi_handle, (uint8_t*)data, len) != HAL_OK)
    {
        log_w("SPI DMA写入启动失败");
        sg_dma_transfer_done = 1;
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }
    
    /* 等待DMA传输完成（带超时检测） */
    timeout_start = HAL_GetTick();
    while (sg_dma_transfer_done == 0)
    {
        if ((HAL_GetTick() - timeout_start) > timeout_ms)
        {
            /* 超时：强制复位DMA状态 */
            log_e("DMA传输超时");
            sg_dma_transfer_done = 1;
            HAL_SPI_DMAStop(&sg_spi_handle);
            sg_spi_state = SPI_STATE_ERROR;
            return SPI_ERROR_TIMEOUT;
        }
    }
    
    sg_spi_state = SPI_STATE_IDLE;
    log_d("SPI DMA写入成功: len=%d", len);
#else
    /* 普通模式：阻塞传输 */
    if (HAL_SPI_Transmit(&sg_spi_handle, (uint8_t *)data, len, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_w("SPI写入失败");
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }

    sg_spi_state = SPI_STATE_IDLE;
    log_d("SPI写入成功: len=%d", len);
#endif

    return SPI_OK;
}

/**
 * @brief       SPI读多字节
 *
 * @param[out]  data 数据指针
 * @param[in]   len  数据长度
 *
 * @retval      SPI_OK         读取成功
 * @retval      SPI_ERROR_PARAM 参数错误
 * @retval      SPI_ERROR_HAL   HAL库错误
 *
 * @note        只接收不发送（发送0xFF作为时钟）
 */
SPI_Error_e spi_read(uint8_t *data, uint16_t len)
{
    /* 参数检查 */
    if (data == NULL || len == 0)
    {
        log_w("读取参数错误: data=%p, len=%d", data, len);
        return SPI_ERROR_PARAM;
    }

    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }

    sg_spi_state = SPI_STATE_RX;

    /* 接收数据 */
    if (HAL_SPI_Receive(&sg_spi_handle, data, len, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_w("SPI读取失败");
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }

    sg_spi_state = SPI_STATE_IDLE;
    log_d("SPI读取成功: len=%d", len);

    return SPI_OK;
}

/**
 * @brief       SPI全双工收发
 *
 * @param[in]   tx_data 发送数据指针
 * @param[out]  rx_data 接收数据指针
 * @param[in]   len     数据长度
 *
 * @retval      SPI_OK         收发成功
 * @retval      SPI_ERROR_PARAM 参数错误
 * @retval      SPI_ERROR_HAL   HAL库错误
 *
 * @note        同时发送和接收数据
 */
SPI_Error_e spi_write_read(const uint8_t *tx_data, uint8_t *rx_data, uint16_t len)
{
    /* 参数检查 */
    if (tx_data == NULL || rx_data == NULL || len == 0)
    {
        log_w("收发参数错误: tx_data=%p, rx_data=%p, len=%d", tx_data, rx_data, len);
        return SPI_ERROR_PARAM;
    }

    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }

    sg_spi_state = SPI_STATE_TX_RX;

    /* 全双工收发 */
    if (HAL_SPI_TransmitReceive(&sg_spi_handle, (uint8_t *)tx_data, rx_data, len, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_w("SPI收发失败");
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }

    sg_spi_state = SPI_STATE_IDLE;
    log_d("SPI收发成功: len=%d", len);

    return SPI_OK;
}

/**
 * @brief       获取SPI当前状态
 *
 * @param       无
 *
 * @retval      SPI_State_e 当前状态
 *
 * @note        用于状态监控和调试
 */
SPI_State_e spi_get_state(void)
{
    return sg_spi_state;
}

/**
 * @brief       检查SPI是否忙
 *
 * @param       无
 *
 * @retval      1 忙
 * @retval      0 空闲
 *
 * @note        用于判断是否可以开始新的传输
 */
uint8_t spi_is_busy(void)
{
    return (sg_spi_state != SPI_STATE_IDLE);
}

/**
 * @brief       打印SPI当前状态（用于调试）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        显示初始化状态、当前状态、配置信息等
 */
void spi_print_status(void)
{
    const char *state_str[] = {"IDLE", "BUSY", "TX", "RX", "TX_RX", "ERROR"};

    log_i("SPI当前状态:");
    log_i("  初始化状态: %s", sg_spi_initialized ? "已初始化" : "未初始化");
    log_i("  当前状态: %s", state_str[sg_spi_state]);
    log_i("  工作模式: 主机模式");
    log_i("  数据位宽: %s", SPI_DATASIZE == SPI_DATASIZE_8BIT ? "8位" : "16位");
}

#if (SPI_USE_DMA == 1)
/**
 * @brief       DMA传输完成回调函数
 *
 * @param[in]   hspi SPI句柄指针
 *
 * @retval      无
 *
 * @note        在DMA传输完成时由HAL库调用
 */
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if (hspi->Instance == sg_spi_handle.Instance)
    {
        sg_dma_transfer_done = 1;  /* 设置传输完成标志 */
    }
}

/**
 * @brief       SPI DMA中断处理函数
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        供stm32f1xx_it.c调用，提高代码可移植性
 */
void SPI_DMA_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&sg_spi_dma_tx_handle);
}
#endif
